/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2015 ForgeRock AS. */ package org.forgerock.opendj.maven; import static java.lang.String.*; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.LifecyclePhase; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; /** * Generate a class path suitable for the Class-Path header of a Manifest file, * allowing to filter on included jars, using excludes/includes properties. * <p> * There is a single goal that generates a property given by 'classPathProperty' * parameter, with the generated classpath as the value. */ @Mojo(name = "generate-manifest", defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME) public final class GenerateManifestClassPathMojo extends AbstractMojo { private static final int MAX_LINE_LENGTH = 72; private static final String HEADER_CLASSPATH = "Class-Path:"; /** * The Maven Project. */ @Parameter(property = "project", required = true, readonly = true) private MavenProject project; /** * A property to set to the content of the generated classpath string. */ @Parameter(required = true) private String classPathProperty; /** * List of artifacts to exclude from the classpath. Each item must be of format "groupId:artifactId". */ @Parameter private List<String> excludes; /** * List of artifacts to include in the classpath. Each item must be of format "groupId:artifactId". */ @Parameter private List<String> includes; /** * List of additional JARs to include in the classpath. Each item must be of format "file.jar". */ @Parameter private List<String> additionalJars; /** * Name of product jar, e.g. "OpenDJ". */ @Parameter private String productJarName; /** * List of supported locales, separated by a ",". * <p> * Example: "fr,es,de" */ @Parameter private String supportedLocales; /** {@inheritDoc} */ @Override public void execute() throws MojoExecutionException, MojoFailureException { try { String classPath = getClasspath(); getLog().info( format("Setting the classpath property: [%s] (debug to see actual value)", classPathProperty)); getLog().debug(String.format("Setting the classpath property %s to:\n%s", classPathProperty, classPath)); project.getProperties().put(classPathProperty, classPath); } catch (DependencyResolutionRequiredException e) { getLog().error( String.format("Unable to set the classpath property %s, an error occured", classPathProperty)); throw new MojoFailureException(e.getMessage(), e); } } /** * Get the classpath. * <p> * The returned value is conform to Manifest Header syntax, where line length must be at most 72 bytes. * * @return the classpath string * @throws DependencyResolutionRequiredException */ private String getClasspath() throws DependencyResolutionRequiredException { final List<String> classpathItems = getClasspathItems(); final StringBuilder classpath = new StringBuilder(HEADER_CLASSPATH); for (String item : classpathItems) { classpath.append(" ").append(item); } int index = MAX_LINE_LENGTH - 2; while (index <= classpath.length()) { classpath.insert(index, "\n "); index += MAX_LINE_LENGTH - 1; } return classpath.toString(); } private List<String> getClasspathItems() throws DependencyResolutionRequiredException { final List<String> classpathItems = new ArrayList<>(); // add project dependencies for (String artifactFile : project.getRuntimeClasspathElements()) { final File file = new File(artifactFile); if (file.getAbsoluteFile().isFile()) { final Artifact artifact = findArtifactWithFile(project.getArtifacts(), file); if (isAccepted(artifact)) { final String artifactString = artifact.getArtifactId() + "." + artifact.getType(); classpathItems.add(artifactString); } } } // add product jars, with localized versions Collections.sort(classpathItems); if (productJarName != null) { if (supportedLocales != null) { String[] locales = supportedLocales.split(","); for (int i = locales.length - 1; i >= 0; i--) { classpathItems.add(0, productJarName + "_" + locales[i] + ".jar"); } } classpathItems.add(0, productJarName + ".jar"); } // add additional JARs if (additionalJars != null) { classpathItems.addAll(additionalJars); } return classpathItems; } private boolean isAccepted(Artifact artifact) { String artifactString = artifact.getGroupId() + ":" + artifact.getArtifactId(); if (includes != null) { if (containsIgnoreCase(includes, artifactString)) { return true; } if (!includes.isEmpty()) { return false; } } return !containsIgnoreCase(excludes, artifactString); } private boolean containsIgnoreCase(List<String> strings, String toFind) { if (strings == null) { return false; } for (String s : strings) { if (toFind.equalsIgnoreCase(s)) { return true; } } return false; } private Artifact findArtifactWithFile(Set<Artifact> artifacts, File file) { for (Artifact artifact : artifacts) { if (artifact.getFile() != null && artifact.getFile().equals(file)) { return artifact; } } return null; } }